package com.elot.core.dao;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.PrimaryKey;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jdo.JdoTemplate;
import org.springframework.stereotype.Repository;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

@Repository("genericDao")
public class GenericJdoDaoImpl implements GenericDao {
	
	protected final Logger log = LoggerFactory.getLogger(getClass());

	@Autowired
	private PersistenceManagerFactory pmf;
	public void setPmf(PersistenceManagerFactory pmf) {
		this.pmf = pmf;
	}
	public PersistenceManagerFactory getPmf() {
		return pmf;
	}
	
	private JdoTemplate jdoTemplate;
	@PostConstruct
	void createJdoTemplate() {
		jdoTemplate = new JdoTemplate(pmf);
	}
	
	
	public <T> T findObject(Class<T> c, Long pk) {
		T t = jdoTemplate.getObjectById(c, new KeyFactory.Builder(c.getSimpleName(), pk).getKey() );
		return jdoTemplate.detachCopy(t);
	}
	
	public <T> T findObject(Class<T> c, Key pk) {
		T t = jdoTemplate.getObjectById(c, pk);
		return jdoTemplate.detachCopy(t);
	}

	public <T> T saveObject(T object) {
		return jdoTemplate.makePersistent(object);
	}

//	public <T> void updateObject(T object) {
//		object = getPersistenceObject(object);
//		jdoTemplate.refresh(object);
//	}

	public <T> void removeObject(Class<T> c, Long pk) {
		Object object = jdoTemplate.getObjectById(new KeyFactory.Builder(c.getSimpleName(), pk).getKey());
		jdoTemplate.deletePersistent(object);
	}
	
	public void removeObject(Key pk) {
		Object object = jdoTemplate.getObjectById(pk);
		jdoTemplate.deletePersistent(object);
	}

	public <T> Collection<T> findAllObjects(Class<T> c) {
		Collection<T> ct = jdoTemplate.find(c);
		return jdoTemplate.detachCopyAll(ct);
	}

	public <T> Collection<T> findObjectsByParams(Class<T> c, Map<String, Object> params) {
		Map<String, Object> queryParams = new HashMap<String, Object>();
		StringBuffer filter = new StringBuffer();
		StringBuffer parameters = new StringBuffer();
		Set<String> keys = params.keySet();
		for (String key : keys) {
			filter.append( !"".equals(filter.toString()) ? " & " : "" ); 
			filter.append( key + " == param"+key );
			parameters.append( !"".equals(parameters.toString()) ? ", " : "" );
			parameters.append( params.get(key).getClass().getName() + " param"+key );
			queryParams.put("param"+key, params.get(key));
		}
		Collection<T> ct = jdoTemplate.find(c, filter.toString(), parameters.toString(), queryParams);
		return jdoTemplate.detachCopyAll(ct);
	}
	
	@SuppressWarnings("unchecked")
	public <T> Collection<T> findByTemplate(T template) {
		
		String propertyName = null;
		String methodName = null;
		Map<String, Object> params = new HashMap<String, Object>();
		Object value = null;
		for (Method method : template.getClass().getMethods()) {
			
			methodName = method.getName();
			propertyName = methodName.startsWith("get") ? methodName.replaceFirst("get", "") : null;
			
			if (propertyName!=null && !"".equals(propertyName) && !"Class".equals(propertyName)) {
				try {
					value = method.invoke(template);
					if (value!=null && value.getClass().isAnnotationPresent(PersistenceCapable.class)) {
						for (Method m : value.getClass().getMethods()) {
							if (m.getName().startsWith("get") && m.isAnnotationPresent(PrimaryKey.class)) {
								value = m.invoke(value);
								break;
							}
						}
					}
					if (value!=null) { //first letter tolower
						String finalPropertyName = propertyName.substring(0, 1).toLowerCase();
						finalPropertyName += propertyName.substring(1, propertyName.length()); 
						params.put(finalPropertyName, value);
					}
				} catch (Exception ignore) {}
			}
			
		}
		
		Collection<T> ct = (Collection<T>) findObjectsByParams(template.getClass(), params);
		return jdoTemplate.detachCopyAll(ct);
	}
	
//	@SuppressWarnings("unchecked")
//	private <T> T getPersistenceObject(T object) {
//		T result = null;;
//		
//		if (object.getClass().isAnnotationPresent(PersistenceCapable.class)) {
//			try {
//				boolean isPersistence = (Boolean)object.getClass().getMethod("jdoIsPersistent").invoke(object);
//				if (!isPersistence) {
//					Key key = null;
//					for (Method m : object.getClass().getMethods()) {
//						if (m.isAnnotationPresent(PrimaryKey.class)) {
//							key = (Key)m.invoke(object);
//							break;
//						}
//					}
//					if (key!=null) {
//						result = (T) findObject(object.getClass(), key);
//						for (Method methodGet : object.getClass().getMethods()) {
//							String methodNameSet = null;
//							if (methodGet.getName().startsWith("get"))
//								methodNameSet = methodGet.getName().replace("get", "set");
//							else if (methodGet.getName().startsWith("is")) 
//								methodNameSet = methodGet.getName().replace("is", "set");
//							
//							if (methodNameSet!=null) {
//								Method methodSet = result.getClass().getMethod(methodNameSet, methodGet.invoke(result).getClass() );
//								methodSet.invoke(result, methodGet.invoke(object));
//							}
//						}
//					}
//				} else {
//					result = object;
//				}
//			} catch (Exception e) {
//				log.error(e.getMessage(), e);
//			}
//		}
//		return result;
//	}
}
